package cn.edu.hit;

import cn.edu.hit.bean.RpcRequest;
import cn.edu.hit.bean.RpcResponse;
import cn.edu.hit.coder.RpcDecoder;
import cn.edu.hit.coder.RpcEncoder;
import cn.edu.hit.registry.ServiceRegistry;
import cn.edu.hit.util.StringUtil;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import org.apache.commons.collections4.MapUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

import java.util.HashMap;
import java.util.Map;

/**
 * Created by 朱才海 on 2017/12/18.
 */
public class RpcServer implements ApplicationContextAware, InitializingBean {

    private static final Logger LOGGER = LoggerFactory.getLogger(RpcServer.class);

    private String serviceAddress;

    private ServiceRegistry serviceRegistry;

    //存放服务名称与服务对象的映射关系
    private Map<String, Object> handlerMap = new HashMap<String, Object>();

    public RpcServer(String serviceAddress){
        this.serviceAddress = serviceAddress;
    }

    public RpcServer(String serviceAddress, ServiceRegistry serviceRegistry){
        this.serviceAddress = serviceAddress;
        this.serviceRegistry = serviceRegistry;
    }


    public void afterPropertiesSet() throws Exception {
         EventLoopGroup bossGroup = new NioEventLoopGroup();
         EventLoopGroup workerGroup = new NioEventLoopGroup();
         try{
             //创建并初始化 Netty 服务端 Bootstrap 对象
             ServerBootstrap bootstrap = new ServerBootstrap();
             bootstrap.group(bossGroup, workerGroup);
             bootstrap.channel(NioServerSocketChannel.class);
             bootstrap.childHandler(new ChannelInitializer<SocketChannel>() {
                 protected void initChannel(SocketChannel socketChannel) throws Exception {
                     ChannelPipeline pipeline = socketChannel.pipeline();
                     pipeline.addLast(new RpcDecoder(RpcRequest.class));
                     pipeline.addLast(new RpcEncoder(RpcResponse.class));
                     //TODO params
                     pipeline.addLast(new RpcServerHandler(handlerMap));
                 }
             });

             bootstrap.option(ChannelOption.SO_BACKLOG, 1024);
             bootstrap.childOption(ChannelOption.SO_KEEPALIVE, true);
             //获取RPC服务器的ip地址和端口号
             String[]  addressArray = StringUtil.split(serviceAddress, ":");
             String ip = addressArray[0];
             int port = Integer.parseInt(addressArray[1]);
             //启动RPC服务器
             ChannelFuture future = bootstrap.bind(ip, port).sync();
             //注册RPC服务地址
             if(serviceRegistry != null){
                 for(String interfaceName : handlerMap.keySet()){
                     serviceRegistry.register(interfaceName, serviceAddress);
                     LOGGER.debug("register service:" + interfaceName + "->" + serviceAddress);
                 }
             }
             LOGGER.debug("server started on port:" + port);
             //关闭RPC服务器
             future.channel().closeFuture().sync();
         }finally {
             workerGroup.shutdownGracefully();
             bossGroup.shutdownGracefully();
         }
    }

    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        //扫描带有 RpcService 注解的类并初始化 handlerMao对象
        Map<String, Object> serviceBeanMap = applicationContext.getBeansWithAnnotation(RpcService.class);
        if(MapUtils.isNotEmpty(serviceBeanMap)){
            for(Object serviceBean : serviceBeanMap.values()){
                RpcService rpcService = serviceBean.getClass().getAnnotation(RpcService.class);
                String serviceName = rpcService.value().getName();
                String serviceVersion = rpcService.version();
                if(StringUtil.isNotEmpty(serviceVersion)){
                    serviceName += "-" + serviceVersion;
                }
                handlerMap.put(serviceName, serviceBean);
            }
        }
    }
}
